package cn.js189.uqc.util;

import javax.crypto.*;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

public class Des3 {

	private static final char[] hexDigits = "0123456789abcdef".toCharArray();

	private Des3(){
		throw new IllegalStateException("Utility class");
	}

	public static byte[] des3EncodeECB(byte[] key, byte[] data)  throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException  {
		Key deskey = null;
		DESedeKeySpec spec = new DESedeKeySpec(key);
		SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
		deskey = keyfactory.generateSecret(spec);
		Cipher cipher = Cipher.getInstance("desede/ECB/PKCS5Padding");
		cipher.init(1, deskey);
		return cipher.doFinal(data);
	}

	public static byte[] ees3DecodeECB(byte[] key, byte[] data)  throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException  {
		Key deskey = null;
		DESedeKeySpec spec = new DESedeKeySpec(key);
		SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
		deskey = keyfactory.generateSecret(spec);
		Cipher cipher = Cipher.getInstance("desede/ECB/PKCS5Padding");
		cipher.init(2, deskey);
		return cipher.doFinal(data);
	}

	public static byte[] des3EncodeCBC(byte[] key, byte[] keyiv, byte[] data)  throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
		Key deskey = null;
		DESedeKeySpec spec = new DESedeKeySpec(key);
		SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
		deskey = keyfactory.generateSecret(spec);
		Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
		IvParameterSpec ips = new IvParameterSpec(keyiv);
		cipher.init(1, deskey, ips);
		return cipher.doFinal(data);
	}

	public static byte[] des3DecodeCBC(byte[] key, byte[] keyiv, byte[] data) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
		Key deskey = null;
		DESedeKeySpec spec = new DESedeKeySpec(key);
		SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
		deskey = keyfactory.generateSecret(spec);
		Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
		IvParameterSpec ips = new IvParameterSpec(keyiv);
		cipher.init(2, deskey, ips);
		return cipher.doFinal(data);
	}

	private static int parse(char c) {
		if (c >= 'a') {
			return c - 97 + 10 & 15;
		} else {
			return c >= 'A' ? c - 65 + 10 & 15 : c - 48 & 15;
		}
	}

	public static String byte2HexStr(byte[] bytes) {
		if (bytes == null) {
			return null;
		} else {
			int j = bytes.length;
			char[] str = new char[j * 2];
			int k = 0;
			
			for (byte byte0 : bytes) {
				str[k++] = hexDigits[byte0 >>> 4 & 15];
				str[k++] = hexDigits[byte0 & 15];
			}

			return (new String(str)).trim();
		}
	}

	public static byte[] hexString2Bytes(String hexstr) {
		byte[] b = new byte[hexstr.length() / 2];
		int j = 0;

		for(int i = 0; i < b.length; ++i) {
			char c0 = hexstr.charAt(j++);
			char c1 = hexstr.charAt(j++);
			b[i] = (byte)(parse(c0) << 4 | parse(c1));
		}

		return b;
	}

	public static byte[] des3EncodeCBCBytes(String key, String keyiv, String srcData) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException {
		return des3EncodeCBC(key.getBytes(StandardCharsets.UTF_8), keyiv.getBytes(StandardCharsets.UTF_8), srcData.getBytes(StandardCharsets.UTF_8));
	}

	public static String des3EncodeCBC(String key, String keyiv, String srcData) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException {
		return byte2HexStr(des3EncodeCBC(key.getBytes(StandardCharsets.UTF_8), keyiv.getBytes(StandardCharsets.UTF_8), srcData.getBytes(StandardCharsets.UTF_8)));
	}

	public static String des3DecodeCBC(String key, String keyiv, String srcData) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException {
		byte[] decodeByte = des3DecodeCBC(key.getBytes(StandardCharsets.UTF_8), keyiv.getBytes(StandardCharsets.UTF_8), hexString2Bytes(srcData));
		return new String(decodeByte, StandardCharsets.UTF_8);
	}
}
